package com.kgc.novel.filters;

import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson2.JSON;
import com.kgc.novel.common.Res;
import com.kgc.novel.config.AuthJwtProperties;
import com.kgc.novel.constants.JwtConstant;
import com.kgc.novel.util.JwtUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import javax.annotation.Resource;
import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.Map;

/**
 * 全局用户鉴权过滤器
 *
 * @author lds
 */
@Slf4j
@Component
public class GlobalTokenAuthFilter implements GlobalFilter, Ordered {

    @Resource
    private AuthJwtProperties authJwtProperties;

    @Resource
    private JwtUtil jwtUtil;


    private Mono<Void> getVoidMono(ServerHttpResponse serverHttpResponse, Res apiResult) {
        serverHttpResponse.getHeaders().add("Content-Type", "application/json;charset=UTF-8");

        DataBuffer dataBuffer = serverHttpResponse.bufferFactory().wrap(JSON.toJSONString(apiResult).getBytes());
        return serverHttpResponse.writeWith(Flux.just(dataBuffer));
    }

    private Mono<Void> unauthorizedResponse(ServerHttpResponse response, HttpStatus statusCode, String errMsg) {
        response.setStatusCode(statusCode);
        response.getHeaders().set("Content-Type", "application/json;charset=UTF-8");

        byte[] bytes = JSONUtil.toJsonPrettyStr(Res.returnTokenErr(errMsg)).getBytes(StandardCharsets.UTF_8);
        DataBuffer dataBuffer = response.bufferFactory().wrap(bytes);
        return response.writeWith(Mono.just(dataBuffer));
    }

    private boolean checkIgnoreUrl(String reqUrl) {
        List<String> whiteList = authJwtProperties.getWhiteList();
        boolean isExclusionUrl = false;
        for (String url : whiteList) {
            if (reqUrl.contains(url)) {
                isExclusionUrl = true;
                break;
            }
        }
        return isExclusionUrl;
    }


    //请求token
    private String getToken(ServerHttpRequest request) {
        String token = request.getHeaders().getFirst("token");
        if (StringUtils.isBlank(token)) {
            token = request.getQueryParams().getFirst("token");
        }
        if (StringUtils.isNotBlank(token) && token.startsWith(JwtConstant.PREFIX)) {
            token = token.replaceFirst(JwtConstant.PREFIX, "");
        }
        return token;
    }

    private String getRefreshToken(ServerHttpRequest request) {
        String token = request.getHeaders().getFirst("refreshToken");
        if (StringUtils.isEmpty(token)) {
            token = request.getQueryParams().getFirst("refreshToken");
        }
        if (StringUtils.isNotBlank(token) && token.startsWith(JwtConstant.PREFIX)) {
            token = token.replaceFirst(JwtConstant.PREFIX, "");
        }
        return token;
    }

    @Override
    public Mono<Void> filter(
            ServerWebExchange exchange,
            GatewayFilterChain chain) {
        //获得请求和响应对象
        ServerHttpRequest request = exchange.getRequest();
        ServerHttpResponse response = exchange.getResponse();
        String path = request.getURI().getPath();

        //1、判断是否是过滤的路径， 是的话就放行
        if (checkIgnoreUrl(path)) {
            log.info("白名单，放行{}", request.getURI().getPath());
            return chain.filter(exchange);
        }

        //获得请求头中Authorization token信息
        String token = getToken(request);
        String refreshToken = getRefreshToken(request);

        if (StringUtils.isBlank(token) || StringUtils.isBlank(refreshToken)) {
            return getVoidMono(response, Res.returnTokenErr("jwt鉴权失败，令牌不能为空"));
        }

        Boolean isAccessToken = jwtUtil.isTokenNotExistCache(token);
        Boolean isRefreshToken = jwtUtil.isRefreshTokenNotExistCache(refreshToken);

        if (isRefreshToken) {
            return getVoidMono(response, Res.returnTokenErr("jwt鉴权失败，令牌无效，请登录"));
        } else if (isAccessToken) {
            String userId = jwtUtil.getUserIdFromToken(token);
            String username = jwtUtil.getUserNameFromToken(token);
            jwtUtil.generateTokenAndRefreshToken(userId, username);
        }

        Boolean isValid = jwtUtil.isTokenExpired(refreshToken);

        if (isValid) {
            return getVoidMono(response, Res.returnTokenErr("jwt鉴权失败，令牌过期，请登录"));
        }

        return chain.filter(exchange); //表示继续向下执行,到下游微服务
    }

    //过滤器的优先级，数值越小，优先级越高
    @Override
    public int getOrder() {
        return 0;
    }
}
